﻿//
// (C) Copyright 2009 Irantha Suwandarathna (iranthas@hotmail.com)
// All rights reserved.
//

/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EffiProz.Core.Lib
{
    public class EfzArrayList : BaseList, EfzList
    {
        //fredt@users
        /*
            private static Reporter reporter = new Reporter();

            private static class Reporter {

                private static int initCounter   = 0;
                private static int updateCounter = 0;

                Reporter() {

                    try {
                        System.runFinalizersOnExit(true);
                    } catch (SecurityException e) {}
                }

                protected void finalize() {

                    System.out.println("HsqlArrayList init count: " + initCounter);
                    System.out.println("HsqlArrayList update count: "
                                       + updateCounter);
                }
            }
        */
        private const int DEFAULT_INITIAL_CAPACITY = 10;
        private const float DEFAULT_RESIZE_FACTOR = 2.0f;
        public Object[] elementData;
        Object[] reserveElementData;
        private bool minimizeOnClear;

        public EfzArrayList(Object[] data, int count)
        {
            elementData = data;
            elementCount = count;
        }

        /** Creates a new instance of HsqlArrayList */
        public EfzArrayList()
        {

            //        reporter.initCounter++;
            elementData = new Object[DEFAULT_INITIAL_CAPACITY];
        }

        /**
         * Creates a new instance of HsqlArrayList that minimizes the size when
         * empty
         */
        public EfzArrayList(bool minimize)
        {

            //        reporter.initCounter++;
            elementData = new Object[DEFAULT_INITIAL_CAPACITY];
            minimizeOnClear = minimize;
        }

        /** Creates a new instance with the given initial capacity */
        public EfzArrayList(int initialCapacity)
        {

            //        reporter.initCounter++;
            if (initialCapacity < 0)
            {
                throw new ArgumentOutOfRangeException(
                    "Invalid initial capacity given");
            }

            if (initialCapacity == 0)
            {
                elementData = new Object[1];
            }
            else
            {
                elementData = new Object[initialCapacity];
            }
        }

        /** Inserts an element at the given index */
        public void add(int index, Object element)
        {

            //        reporter.updateCounter++;
            if (index > elementCount)
            {
                throw new IndexOutOfRangeException("Index out of bounds: "
                                                    + index + ">" + elementCount);
            }

            if (index < 0)
            {
                throw new IndexOutOfRangeException("Index out of bounds: "
                                                    + index + " < 0");
            }

            if (elementCount >= elementData.Length)
            {
                increaseCapacity();
            }

            for (int i = elementCount; i > index; i--)
            {
                elementData[i] = elementData[i - 1];
            }

            elementData[index] = element;

            elementCount++;
        }

        /** Appends an element to the end of the list */
        public override bool add(Object element)
        {

            //        reporter.updateCounter++;
            if (elementCount >= elementData.Length)
            {
                increaseCapacity();
            }

            elementData[elementCount] = element;

            elementCount++;

            return true;
        }

        /** Gets the element at given position */
        public override Object get(int index)
        {

            if (index >= elementCount)
            {
                throw new IndexOutOfRangeException("Index out of bounds: "
                                                    + index + " >= "
                                                    + elementCount);
            }

            if (index < 0)
            {
                throw new IndexOutOfRangeException("Index out of bounds: "
                                                    + index + " < 0");
            }

            return elementData[index];
        }

        /** returns the index of given object or -1 if not found */
        public override int indexOf(Object o)
        {

            if (o == null)
            {
                for (int i = 0; i < elementCount; i++)
                {
                    if (elementData[i] == null)
                    {
                        return i;
                    }
                }

                return -1;
            }

            for (int i = 0; i < elementCount; i++)
            {
                if (o.Equals(elementData[i]))
                {
                    return i;
                }
            }

            return -1;
        }

        /** Removes and returns the element at given position */
        public override Object remove(int index)
        {

            if (index >= elementCount)
            {
                throw new IndexOutOfRangeException("Index out of bounds: "
                                                    + index + " >= "
                                                    + elementCount);
            }

            if (index < 0)
            {
                throw new IndexOutOfRangeException("Index out of bounds: "
                                                    + index + " < 0");
            }

            Object removedObj = elementData[index];

            for (int i = index; i < elementCount - 1; i++)
            {
                elementData[i] = elementData[i + 1];
            }

            elementCount--;

            if (elementCount == 0)
            {
                clear();
            }
            else
            {
                elementData[elementCount] = null;
            }

            return removedObj;
        }

        /** Replaces the element at given position */
        public Object set(int index, Object element)
        {

            if (index >= elementCount)
            {
                throw new IndexOutOfRangeException("Index out of bounds: "
                                                    + index + " >= "
                                                    + elementCount);
            }

            if (index < 0)
            {
                throw new IndexOutOfRangeException("Index out of bounds: "
                                                    + index + " < 0");
            }

            Object replacedObj = elementData[index];

            elementData[index] = element;

            return replacedObj;
        }

        /** Returns the number of elements in the array list */
        public override int size()
        {
            return elementCount;
        }

        private void increaseCapacity()
        {

            int baseSize = elementData.Length == 0 ? 1
                                                   : elementData.Length;

            baseSize = (int)(baseSize * DEFAULT_RESIZE_FACTOR);

            resize(baseSize);
        }

        private void resize(int baseSize)
        {

            if (baseSize == elementData.Length)
            {
                return;
            }

            Object[] newArray = (Object[])Array.CreateInstance(
                elementData.GetType().GetElementType(), baseSize);
            int count = elementData.Length > newArray.Length ? newArray.Length
                                                             : elementData.Length;

            Array.Copy(elementData, 0, newArray, 0, count);

            if (minimizeOnClear && reserveElementData == null)
            {
                ArrayUtil.clearArray(ArrayUtil.CLASS_CODE_OBJECT, elementData, 0,
                                     elementData.Length);

                reserveElementData = elementData;
            }

            elementData = newArray;
        }

        /** Trims the array to be the same size as the number of elements. */
        public void trim()
        {

            // 0 size array is possible
            resize(elementCount);
        }

        // fredt@users
        public void clear()
        {

            if (minimizeOnClear && reserveElementData != null)
            {
                elementData = reserveElementData;
                reserveElementData = null;
                elementCount = 0;

                return;
            }

            for (int i = 0; i < elementCount; i++)
            {
                elementData[i] = null;
            }

            elementCount = 0;
        }

        public void setSize(int newSize)
        {

            if (newSize == 0)
            {
                clear();

                return;
            }

            if (newSize <= elementCount)
            {
                for (int i = newSize; i < elementCount; i++)
                {
                    elementData[i] = null;
                }

                elementCount = newSize;

                return;
            }

            for (; newSize > elementData.Length; )
            {
                increaseCapacity();
            }

            elementCount = newSize;
        }

        // fredt@users
        public Object[] toArray()
        {

            Object[] newArray = (Object[])Array.CreateInstance(
                elementData.GetType().GetElementType(), elementCount);

            Array.Copy(elementData, 0, newArray, 0, elementCount);

            return newArray;
        }

        public Object[] toArray(int start, int limit)
        {

            Object[] newArray = (Object[])Array.CreateInstance(
                elementData.GetType().GetElementType(), limit - start);

            Array.Copy(elementData, start, newArray, 0, limit - start);

            return newArray;
        }

        /**
         * Copies all elements of the list to a[]. It is assumed a[] is of the
         * correct type. If a[] is too small, a new array or the same type is
         * returned. If a[] is larger, only the list elements are copied and no
         * other change is made to the array.
         * Differs from the implementation in java.util.ArrayList in the second
         * aspect.
         */
        public Object toArray(Object a)
        {

            if (((Array)a).Length < elementCount)
            {
                a = Array.CreateInstance(a.GetType().GetElementType(),
                                      elementCount);
            }

            Array.Copy((Array)elementData, 0, (Array)a, 0, elementCount);

            return a;
        }

        public void sort(ObjectComparator c)
        {

            if (elementCount < 2)
            {
                return;
            }

            Sort.sort(elementData, c, 0, elementCount - 1);
        }

        public Object[] getArray()
        {
            return elementData;
        }
    }
}
